package com.gemini.main.common.orm.jpa.repository;

import com.gemini.main.common.orm.jpa.bean.Filter;
import com.gemini.main.common.orm.jpa.bean.PageFilter;
import com.gemini.main.common.orm.jpa.bean.Query;
import org.hibernate.query.NativeQuery;
import org.hibernate.transform.Transformers;
import org.springframework.dao.IncorrectResultSizeDataAccessException;
import org.springframework.transaction.annotation.Transactional;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import java.util.List;
import java.util.Map;

/**
 * Wisdytech.cn
 * com.wisdytech.linkmes.common.orm.jpa.repository.BaseCustomRepositoryImpl
 *
 * @author hailin.zhl
 * @date 2018/11/02
 */
public class BaseCustomRepositoryImpl implements BaseCustomRepository {


    @PersistenceContext
    private EntityManager em;

    @SuppressWarnings("unchecked")
    @Override
    public <T> List<T> findPageByJqpl(String jpql, String countJpql, Map<String, Object> parameters, PageFilter pageFilter, Class<T> type) {
        if (org.apache.commons.lang3.StringUtils.isBlank(jpql)) {
            throw new RuntimeException("sql must not be empty!");
        }
        javax.persistence.Query query = em.createQuery(jpql);
        javax.persistence.Query countQuery = em.createQuery(countJpql);
        if (parameters != null) {
            parameters.forEach((k, v) -> {
                if (v != null) {
                    query.setParameter(k, v);
                    countQuery.setParameter(k, v);
                }
            });
        }
        Object count = countQuery.getSingleResult();
        // set total count
        pageFilter.setTotal(count == null ? 0L : Long.valueOf(count.toString()));

        query.setFirstResult(pageFilter.getStart())
                .setMaxResults(pageFilter.getLimit());
        return (List<T>) query.getResultList();
    }

    @SuppressWarnings("unchecked")
    @Override
    public <T> List<T> findByJqpl(String jpql, Map<String, Object> parameters, Class<T> type) {
        if (org.apache.commons.lang3.StringUtils.isBlank(jpql)) {
            throw new RuntimeException("sql must not be empty!");
        }
        javax.persistence.Query query = em.createQuery(jpql);
        if (parameters != null) {
            parameters.forEach((k, v) -> {
                if (v != null) query.setParameter(k, v);
            });
        }
        return (List<T>) query.getResultList();
    }

    @SuppressWarnings("unchecked")
    @Override
    public <M> List<M> findBySql(String sql, Map<String, Object> parameters, Class<M> type) {
        if (org.apache.commons.lang3.StringUtils.isBlank(sql)) {
            throw new RuntimeException("sql must not be empty!");
        }
        javax.persistence.Query query = em.createNativeQuery(sql);
        if (parameters != null) {
            parameters.forEach((k, v) -> {
                if (v != null) query.setParameter(k, v);
            });
        }
        query.unwrap(NativeQuery.class).setResultTransformer(Transformers.aliasToBean(type));
        return (List<M>) query.getResultList();
    }

    @SuppressWarnings("unchecked")
    @Override
    public <M> M findUniqueBySql(String sql, Map<String, Object> parameters, Class<M> type) {
        if (org.apache.commons.lang3.StringUtils.isBlank(sql)) {
            throw new RuntimeException("sql must not be empty!");
        }
        javax.persistence.Query query = em.createNativeQuery(sql);
        parameters.forEach(query::setParameter);
        query.unwrap(NativeQuery.class).setResultTransformer(Transformers.aliasToBean(type));
        // edit by zhl getSingleResult()如果找不到结果会抛出异常
        //return (M)query.getSingleResult();
        List<M> list = (List<M>) query.getResultList();
        if (list != null && list.size() > 0) {
            if (list.size() == 1) {
                return list.get(0);
            } else {
                throw new IncorrectResultSizeDataAccessException(list.size());
            }
        }
        return null;
    }

    @Transactional
    @Override
    public Integer executeUpdate(String sql, Map<String, Object> parameters) {
        if (org.apache.commons.lang3.StringUtils.isBlank(sql)) {
            throw new RuntimeException("sql must not be empty!");
        }
        javax.persistence.Query query = em.createNativeQuery(sql);
        parameters.forEach(query::setParameter);
        return query.executeUpdate();
    }


    /**
     * @param sql    sql语句
     * @param filter 查询filter
     * @deprecated {@link #findPagesListBySql(String, Map, PageFilter, Class)} instead.
     */
    @SuppressWarnings("unchecked")
    @Override
    @Deprecated
    public List<Map<String, Object>> findPagesListBySql(String sql, Filter filter) {
        if (org.apache.commons.lang3.StringUtils.isBlank(sql)) {
            throw new RuntimeException("sql must not be empty!");
        }
        String countSql = "select count(*) from (" + sql + ") t0";
        // total count query sql
        javax.persistence.Query countQuery = em.createNativeQuery(countSql);
        // query sql
        javax.persistence.Query query = em.createNativeQuery(sql);
        Map<String, Query> queryMap = filter.getQueryMap();
        if (queryMap != null && queryMap.size() > 0) {
            queryMap.forEach((k, v) -> {
                /*switch (v.getCondition()) {
                    case LIKE:
                        countQuery.setParameter(k, "%" + v.getValue() + "%");
                        query.setParameter(k, "%" + v.getValue() + "%");
                        break;
                    case IN:
                        if (v.getValue() instanceof Collection) {
                            countQuery.setParameterList(k, (Collection) v.getValue());
                            query.setParameterList(k, (Collection) v.getValue());
                        } else if (v.getValue().getClass().isArray()) {
                            List listObject = Arrays.asList((Object[]) v.getValue());
                            query.setParameterList(k, listObject);
                        }
                        break;
                    default:
                        countQuery.setParameter(k, v.getValue());
                        query.setParameter(k, v.getValue());
                }*/
                countQuery.setParameter(k, v.getValue());
                query.setParameter(k, v.getValue());
            });
        }
        Object count = countQuery.unwrap(NativeQuery.class).getSingleResult();
        // set total count
        filter.setTotal(count == null ? 0L : Long.valueOf(count.toString()));

        query.unwrap(NativeQuery.class).setFirstResult(filter.getStart())
                .setMaxResults(filter.getLimit())
                .setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP);
        return (List<Map<String, Object>>) query.getResultList();
    }

    @SuppressWarnings("unchecked")
    @Override
    public <E> List<E> findPagesListBySql(String sql, Map<String, Object> paramters, PageFilter filter, Class<E> type) {
        if (org.apache.commons.lang3.StringUtils.isBlank(sql)) {
            throw new RuntimeException("sql must not be empty!");
        }
        String countSql = "select count(*) from (" + sql + ") t0";
        // total count query sql
        javax.persistence.Query countQuery = em.createNativeQuery(countSql);
        // query sql
        javax.persistence.Query query = em.createNativeQuery(sql);
        if (paramters != null && paramters.size() > 0) {
            paramters.forEach((k, v) -> {
                /*switch (v.getCondition()) {
                    case LIKE:
                        countQuery.setParameter(k, "%" + v.getValue() + "%");
                        query.setParameter(k, "%" + v.getValue() + "%");
                        break;
                    case IN:
                        if (v.getValue() instanceof Collection) {
                            countQuery.setParameterList(k, (Collection) v.getValue());
                            query.setParameterList(k, (Collection) v.getValue());
                        } else if (v.getValue().getClass().isArray()) {
                            List listObject = Arrays.asList((Object[]) v.getValue());
                            query.setParameterList(k, listObject);
                        }
                        break;
                    default:
                        countQuery.setParameter(k, v.getValue());
                        query.setParameter(k, v.getValue());
                }*/
                countQuery.setParameter(k, v);
                query.setParameter(k, v);
            });
        }
        Object count = countQuery.unwrap(NativeQuery.class).getSingleResult();
        // set total count
        filter.setTotal(count == null ? 0L : Long.valueOf(count.toString()));

        query.unwrap(NativeQuery.class).setFirstResult(filter.getStart())
                .setMaxResults(filter.getLimit())
                .setResultTransformer(Transformers.aliasToBean(type));
        return (List<E>) query.getResultList();
    }
}
